//---------------------------------------------------------------------
//
//---------------------------------------------------------------------

import java_cup.runtime.*;
import java.util.Vector;

class MyParser extends parser {
	private Lexer m_lexer;
	private ErrorPrinter m_errors;
	private int m_nNumErrors;
	private String m_strLastLexeme;
	private boolean m_bSyntaxError = true;
	private int m_nSavedLineNum;
	private boolean whileForEachOn = false;
	private int whileForEachLevel = -1;
	private boolean isStruct = false;
	private String structName;
	private boolean recursiveStruct;
	private int totalFieldSize;
	private CodeGenerator cg = new CodeGenerator();
	private SymbolTable m_symtab;
	private static int localVarSize = 0;
	private static final String GLOBAL = "%g0";
	private static final String LOCAL = "%fp";
	private boolean decltypeAppear;

	// ----------------------------------------------------------------
	//
	// ----------------------------------------------------------------
	public MyParser(Lexer lexer, ErrorPrinter errors) {
		m_lexer = lexer;
		m_symtab = new SymbolTable();
		m_errors = errors;
		m_nNumErrors = 0;
	}

	// ----------------------------------------------------------------
	//
	// ----------------------------------------------------------------
	public boolean Ok() {
		return m_nNumErrors == 0;
	}

	// ----------------------------------------------------------------
	//
	// ----------------------------------------------------------------
	public Symbol scan() {
		Token t = m_lexer.GetToken();

		// We'll save the last token read for error messages.
		// Sometimes, the token is lost reading for the next
		// token which can be null.
		m_strLastLexeme = t.GetLexeme();

		switch (t.GetCode()) {
		case sym.T_ID:
		case sym.T_ID_U:
		case sym.T_STR_LITERAL:
		case sym.T_FLOAT_LITERAL:
		case sym.T_INT_LITERAL:
			return new Symbol(t.GetCode(), t.GetLexeme());
		default:
			return new Symbol(t.GetCode());
		}
	}

	// ----------------------------------------------------------------
	//
	// ----------------------------------------------------------------
	public void syntax_error(Symbol s) {
	}

	// ----------------------------------------------------------------
	//
	// ----------------------------------------------------------------
	public void report_fatal_error(Symbol s) {
		m_nNumErrors++;
		if (m_bSyntaxError) {
			m_nNumErrors++;

			// It is possible that the error was detected
			// at the end of a line - in which case, s will
			// be null. Instead, we saved the last token
			// read in to give a more meaningful error
			// message.
			m_errors.print(Formatter.toString(ErrorMsg.syntax_error,
					m_strLastLexeme));
		}
	}

	// ----------------------------------------------------------------
	//
	// ----------------------------------------------------------------
	public void unrecovered_syntax_error(Symbol s) {
		report_fatal_error(s);
	}

	// ----------------------------------------------------------------
	//
	// ----------------------------------------------------------------
	public void DisableSyntaxError() {
		m_bSyntaxError = false;
	}

	// ----------------------------------------------------------------
	//
	// ----------------------------------------------------------------
	public void EnableSyntaxError() {
		m_bSyntaxError = true;
	}

	// ----------------------------------------------------------------
	//
	// ----------------------------------------------------------------
	public String GetFile() {
		return m_lexer.getEPFilename();
	}

	// ----------------------------------------------------------------
	//
	// ----------------------------------------------------------------
	public int GetLineNum() {
		return m_lexer.getLineNumber();
	}

	// ----------------------------------------------------------------
	//
	// ----------------------------------------------------------------
	public void SaveLineNum() {
		m_nSavedLineNum = m_lexer.getLineNumber();
	}

	// ----------------------------------------------------------------
	//
	// ----------------------------------------------------------------
	public int GetSavedLineNum() {
		return m_nSavedLineNum;
	}

	public boolean getWhileForEachOn() {
		return whileForEachOn;
	}

	public void setWhileForEachOn(boolean whileForEachOn) {
		this.whileForEachOn = whileForEachOn;
	}

	public int getWhileForEachLevel() {
		return whileForEachLevel;
	}

	public void setWhileForEachLevel(int whileForEachLevel) {
		this.whileForEachLevel = whileForEachLevel;
	}

	public boolean isStruct() {
		return isStruct;
	}

	public void setStruct(boolean isStruct) {
		this.isStruct = isStruct;
	}

	public String getStructName() {
		return structName;
	}

	public void setStructName(String structName) {
		this.structName = structName;
	}
	
	public boolean isRecursiveStruct() {
		return recursiveStruct;
	}

	public void setRecursiveStruct(boolean recursiveStruct) {
		this.recursiveStruct = recursiveStruct;
	}

	public static int getLocalVarSize() {
		return localVarSize;
	}

	public static void setLocalVarSize(int localVarSize) {
		MyParser.localVarSize = localVarSize;
	}

	boolean isDecltypeAppear() {
		return decltypeAppear;
	}

	void setDecltypeAppear(boolean decltypeAppear) {
		this.decltypeAppear = decltypeAppear;
	}

	public int getTotalFieldSize() {
		return totalFieldSize;
	}

	public void setTotalFieldSize(int fieldSize) {
		this.totalFieldSize = fieldSize;
	}

	// ----------------------------------------------------------------
	//
	// ----------------------------------------------------------------
	void DoProgramStart() {
		// Opens the global scope.
		m_symtab.openScope();
        cg.writeFileHeader();
	}

	// ----------------------------------------------------------------
	//
	// ----------------------------------------------------------------
	void DoProgramEnd() {
		m_symtab.closeScope();
		cg.writeAssembly(cg.getBssSB().toString());
		cg.writeAssembly(cg.getRodataSB().toString());
		cg.writeAssembly(cg.getTextSB().toString());
		cg.dispose();
	}

	// ----------------------------------------------------------------
	// Check 8: Added checking for illegal variable initialization
	// ----------------------------------------------------------------
	void DoVarDecl(Vector<IDInfo> lstIDs, Type type, boolean isStatic) {
		// Added in Check 15: Make a copy of the parameter type so we don't
		// modify the original param
		Type paramType = type;
		// Check each STO to see if any of them are ErrorSTO, if so throw them
		// and return right away
		for (int i = 0; i < lstIDs.size(); i++) {
			//System.out.println(lstIDs.get(i).getId()) ;
			if (lstIDs.get(i).getInitVal() != null) {
				if (lstIDs.get(i).getInitVal().isError()) {
					return;
				}
			}
		}

		for (int i = 0; i < lstIDs.size(); i++) {
			String id = lstIDs.get(i).getId();
			boolean arrayFlag = false;
			if (m_symtab.accessLocal(id) != null) {
				m_nNumErrors++;
				if (!isStruct()) {
					m_errors.print(Formatter.toString(ErrorMsg.redeclared_id, id));
				} 
				else {
					m_errors.print(Formatter.toString(ErrorMsg.error13a_Struct, id));
				}
				return;
			} 
			else {
				if (paramType == null) {
					continue;
				}
				// Check 13b: Added extra check for undeclared identifier
				if ( (type.getName().equals(getStructName())) && (lstIDs.get(i).getPtrType() == null ) && (isStruct())){
					m_nNumErrors++;
					m_errors.print(Formatter.toString(ErrorMsg.error13b_Struct, id));
					continue ;
				}
				
				// if it is a pointer type, paramType will become the basetype
				// of the array(if the param is an array of pointers)
				Type newType = paramType;

				/* Check 15: Added to handle PointerType */
				if (lstIDs.get(i).getPtrType() != null) {
					// Recursively gets to the basetype of the pointer and set
					// it as the type passed in
					DoSetPointerBaseType(((PointerType) lstIDs.get(i).getPtrType()), paramType,0);
					paramType = lstIDs.get(i).getPtrType();
					int diff = 0;
					int count1 = 0;
					String diffStar = "";
					if(type.isTypedef()) {
						if(paramType.isPointerType()) {
							// count num of stars in baseType
							if(type.isPointerType()) {
								count1 = ((PointerType)type).numOfStars();
							}
							int count2 = ((PointerType)paramType).numOfStars();
							diff = count2 - count1;
						}
						for(int j = 0; j < diff; j++) {
							diffStar += "*";
						}
						String s = new String(((Type)type).getName());
						paramType.setTypedef(true);
						((Type)paramType).setName(s+diffStar);
						
					}
					// since array's basetype will be set to newType, set
					// newType = paramType
					newType = paramType;
				}
				/* Check 11: Added to handle ArrayType */
				if (lstIDs.get(i).getArrayType() != null) {
					if (lstIDs.get(i).getInitVal() != null) {
						if (lstIDs.get(i).getInitVal().isArray()) {
							boolean successInit = DoArrayInitialization(lstIDs.get(i), paramType);
							if (!successInit) {
								continue;
							}
						}
					}
					ArrayType tmp = (ArrayType) lstIDs.get(i).getArrayType();
					int size = tmp.getNumOfElements();
					newType = new ArrayType(paramType.getName() + "[" + size + "]");
					((ArrayType) newType).setNumOfElements(size);
					((ArrayType) newType).setBaseType(paramType);
					arrayFlag = true;
				}
				// check if it is declared without initialization
				if (!arrayFlag && lstIDs.get(i).getInitVal() != null) {
					// Check 15c: check if it is a PointerType
					if (lstIDs.get(i).getPtrType() != null) {
						boolean ptrInit = DoPointerInitializationCheck((PointerType) lstIDs.get(i).getPtrType(),
										  lstIDs.get(i).getInitVal());
						// continue with the loop to check for other variables
						// to prevent creating a new VarSTO
						if (!ptrInit) {
							m_nNumErrors++;
							m_errors.print(Formatter.toString(
									ErrorMsg.error8_Assign, lstIDs.get(i).getInitVal().getType().getName(),
									((PointerType) lstIDs.get(i).getPtrType()).getName()));
							continue;
						}
					}
					// if type is null(typedef has error, don't continue
					if (lstIDs.get(i).getInitVal().getType() != null) {
						if (!(lstIDs.get(i).getInitVal().getType().isAssignableTo(newType))) {
							m_nNumErrors++;
							m_errors.print(Formatter.toString(ErrorMsg.error8_Assign, lstIDs.get(i).getInitVal().getType().getName(),
											newType.getName()));
							return;
						}
					} 
					else {
						return;
					}
				}
				/* variable did not exist on symbol table locally and
				 * declaration of this variable does not contain any error, 
				 * proceed to put this var onto sym table
				 */
				VarSTO sto = new VarSTO(id, newType);
				if (arrayFlag) {
					sto.setIsNonModLValue();
				} 
				else {
					sto.setIsModLValue();
				}
				if(!isStruct()) {
					if (m_symtab.getLevel() == 1) {
						sto.setGlobal(true);
						sto.setBase(GLOBAL);
						sto.setOffset(sto.getName());
						if (!sto.getType().isArrayType()) { 
							cg.printVarDecl(sto, lstIDs.get(i).getInitVal(), "", isStatic);
						}
						else {
							cg.printVarDecl(sto, null, "", isStatic);
							if(lstIDs.get(i).getInitVal() != null) {
								Vector<STO> initValArray = ((ArraySTO)lstIDs.get(i).getInitVal()).getArrayElements();
								for(int j = 0; j < initValArray.size(); j++) {
									// ExprSTO stores the value of arr[j], pass that into printAssign
									ExprSTO expr = new ExprSTO("");
									setLocalVarSize(getLocalVarSize() + ((ArrayType)sto.getType()).getBaseType().getSize());
									expr.setOffset("-" + getLocalVarSize());
									expr.setBase(LOCAL);
									STO result = DoDesignator2_Array(sto, new ConstSTO(String.valueOf(j), new IntType(), String.valueOf(j)));
									cg.printAssign(result, initValArray.get(j), expr, true) ; // safe to pass null, already checked in printAssign()
								}
							}
						}
					} 
					else {			// local or struct 
						String funcName = m_symtab.getFunc().getName();
						sto.setGlobal(false);
						setLocalVarSize(getLocalVarSize() + sto.getType().getSize());
						if(!isStatic) {
							sto.setBase(LOCAL);
							sto.setOffset("-" + String.valueOf(getLocalVarSize()));
						}
						else {
							sto.setBase(GLOBAL);
							sto.setOffset("." + funcName + sto.getName() + CodeGenerator.getStaticCount());
						}
						if (!sto.getType().isArrayType()) { 
							cg.printVarDecl(sto, lstIDs.get(i).getInitVal(), funcName, isStatic);
						}
						else {// if it is array 
							cg.printVarDecl(sto, null, funcName, isStatic);
							// start working on Array initialization
							if(lstIDs.get(i).getInitVal() != null) {
								Vector<STO> initValArray = ((ArraySTO)lstIDs.get(i).getInitVal()).getArrayElements();
								for(int j = 0; j < initValArray.size(); j++) {
									// ExprSTO stores the value of arr[j], pass that into printAssign
									ExprSTO expr = new ExprSTO("");
									setLocalVarSize(getLocalVarSize() + ((ArrayType)sto.getType()).getBaseType().getSize());
									expr.setOffset("-" + getLocalVarSize());
									expr.setBase(LOCAL);
									STO result = DoDesignator2_Array(sto, new ConstSTO("", new IntType(), String.valueOf(j)));
									cg.printAssign(result, initValArray.get(j), expr, false) ; // safe to pass null, already checked in printAssign()
								}
							}
						}
					}
				}
				m_symtab.insert(sto);
			}
		}
	}

	// ----------------------------------------------------------------
	// Added in Check 15:
	// A recursive function that sets the very basic type of our
	// pointer. RC.cup already handled multiple pointers.
	// ----------------------------------------------------------------
	public void DoSetPointerBaseType(PointerType ptrType, Type t, int count) {
		if (ptrType.getBaseType() == null) {
			ptrType.setBaseType(t);
			ptrType.setName(t.getName());
		} else {
			DoSetPointerBaseType(((PointerType) ptrType.getBaseType()), t,count);
		}
	}

	boolean DoArrayInitialization(IDInfo id, Type t) {
		Vector<STO> v = ((ArraySTO) id.getInitVal()).getArrayElements();
		if (v == null) {
			return false;
		}
		int size = ((ArrayType) id.getArrayType()).getNumOfElements();
		if (v.size() > size) {
			m_nNumErrors++;
			m_errors.print(ErrorMsg.error11_TooManyInitExpr);
			return false;
		}
		for (int i = 0; i < v.size(); i++) {
			if (v.get(i).isError()) {
				return false;
			}
			if (!v.get(i).isConst()) {
				m_nNumErrors++;
				m_errors.print(ErrorMsg.error11_NonConstInitExpr);
				return false;
			}
		}
		for (int i = 0; i < v.size(); i++) {
			if (!v.get(i).getType().isAssignableTo(t)) {
				m_nNumErrors++;
				m_errors.print(Formatter.toString(ErrorMsg.error3b_Assign, v
						.get(i).getType().getName(), t.getName()));
				return false;
			}
		}
		return true;
	}

	private boolean DoPointerInitializationCheck(PointerType ptr, STO init) {
		boolean result = true;
		if (!(init.getType().isArrayType() || init.getType().isPointerType())) {
			result = false;
		} 
		else {
			if (init.getType().isArrayType()) {
				if (!ptr.getBaseType().isEquivalentTo(((ArrayType) init.getType()).getBaseType())) {
					result = false;
				}
			} 
			else { // ptrType
				if (!init.getType().isNullPtrType()) { // nullptr always return true
					if (!ptr.getBaseType().isEquivalentTo(((PointerType) init.getType()).getBaseType())) {
						result = false;
					}
				}
			}
		}
		return result;
	}

	// ----------------------------------------------------------------
	//
	// ----------------------------------------------------------------
	void DoIterationVarDecl(STO sto, STO expr) {
		if (m_symtab.accessLocal(sto.getName()) != null) {
			m_nNumErrors++;
			m_errors.print(Formatter.toString(ErrorMsg.redeclared_id,
					sto.getName()));
		}

		if (!expr.getType().isArrayType()) {
			m_nNumErrors++;
			m_errors.print(ErrorMsg.error12a_Foreach);
			return;
		}
		ArrayType arr = (ArrayType) expr.getType();
		if (((VarSTO) sto).isRef()) {
			// check equivalence
			if (!arr.getBaseType().isEquivalentTo(sto.getType())) {
				m_nNumErrors++;
				m_errors.print(Formatter.toString(ErrorMsg.error12r_Foreach,
						arr.getBaseType().getName(), sto.getName(), sto
								.getType().getName()));
				return;
			}

		} 
		else {
			if (!(arr.getBaseType().isAssignableTo(sto.getType()))) {
				m_nNumErrors++;
				m_errors.print(Formatter.toString(ErrorMsg.error12v_Foreach,
						arr.getBaseType().getName(), sto.getName(), sto.getType().getName()));
				return;
			}
		}
		// int loop counter
		sto.setBase(LOCAL);
		setLocalVarSize(getLocalVarSize() + 4);
		String offset = "-" + String.valueOf(getLocalVarSize());
		sto.setOffset(offset);	
		
		// iteration variable
		setLocalVarSize(getLocalVarSize() + ((ArrayType)expr.getType()).getBaseType().getSize());
		m_symtab.insert(sto);
	}

	// ----------------------------------------------------------------
	//
	// ----------------------------------------------------------------
	void DoExternDecl(Vector<String> lstIDs) {
		for (int i = 0; i < lstIDs.size(); i++) {
			String id = lstIDs.elementAt(i);

			if (m_symtab.accessLocal(id) != null) {
				m_nNumErrors++;
				m_errors.print(Formatter.toString(ErrorMsg.redeclared_id, id));
			}

			VarSTO sto = new VarSTO(id);
			m_symtab.insert(sto);
		}
	}

	// ----------------------------------------------------------------
	// Check 8: Added checking for illegal constant initialization
	// ----------------------------------------------------------------
	void DoConstDecl(Type type, Vector<IDInfo> lstIDs, boolean isStatic) {
		for (int i = 0; i < lstIDs.size(); i++) {
			if (lstIDs.get(i).getInitVal().isError()) {
				return;
			}
			// if not a Const, value is not known at compile time, throw error
			if (!(lstIDs.get(i).getInitVal().isConst())) {
				m_nNumErrors++;
				m_errors.print(Formatter.toString(ErrorMsg.error8_CompileTime,
						lstIDs.get(i).getId()));
				return;

			}
			if (!(lstIDs.get(i).getInitVal().getType().isAssignableTo(type))) {
				m_nNumErrors++;
				m_errors.print(Formatter.toString(ErrorMsg.error8_Assign,
						lstIDs.get(i).getInitVal().getType().getName(),
						type.getName()));
				return;
			}
		}

		for (int i = 0; i < lstIDs.size(); i++) {
			String id = lstIDs.get(i).getId();
			ConstSTO right = (ConstSTO) lstIDs.get(i).getInitVal();

			if (m_symtab.accessLocal(id) != null) {
				m_nNumErrors++;
				m_errors.print(Formatter.toString(ErrorMsg.redeclared_id, id));
			}
			// if the variable is not redeclared(did not exist before on symbol
			// table locally), then
			// create a new ConstSTO with its new const value and store onto
			// symbol table
			else {
				ConstSTO sto = new ConstSTO(id, type, true);
				sto.setValue(right.getValue());
				if (m_symtab.getLevel() == 1) {
					// global 
					sto.setGlobal(true);
					sto.setBase(GLOBAL);
					sto.setOffset(sto.getName());
				} 
				else {
					// local or struct 
					setLocalVarSize(getLocalVarSize() + sto.getType().getSize());
					sto.setBase(LOCAL);
					sto.setOffset("-" + String.valueOf(getLocalVarSize()));
				}
				if(m_symtab.getFunc() != null) {
					cg.printVarDecl(sto, lstIDs.get(i).getInitVal(), m_symtab.getFunc().getName(), isStatic);
				}
				else {	// global
					cg.printVarDecl(sto, lstIDs.get(i).getInitVal(), "", isStatic);
				}
				m_symtab.insert(sto);
			}
		}
	}

	// ----------------------------------------------------------------
	// CHECK 9
	// ----------------------------------------------------------------
	void DoTypedefDecl(Type baseType, Vector<String> lstIDs) {
		for (int i = 0; i < lstIDs.size(); i++) {
			String id = lstIDs.elementAt(i);
			if (m_symtab.accessLocal(id) != null) {
				m_nNumErrors++;
				m_errors.print(Formatter.toString(ErrorMsg.redeclared_id, id));
			} 
			else {
				Type t;
				if (baseType.isInt()) {
					t = new IntType(id);
					t.setSize(baseType.getSize());
				} 
				else if (baseType.isFloat()) {
					t = new FloatType(id);
					t.setSize(baseType.getSize());
				} 
				else if (baseType.isBool()) {
					t = new BoolType(id);
					t.setSize(baseType.getSize());
				} 
				else if (baseType.isArrayType()) {
					baseType.setName(id);
					t = new ArrayType(id);
					t.setSize(baseType.getSize());
					ArrayType tmp = (ArrayType) baseType;
					((ArrayType) t).setBaseType(tmp.getBaseType());
					((ArrayType) t).setNumOfElements(tmp.getNumOfElements());
				} 
				else if (baseType.isFuncPtrType()) {
					t = new FuncPtrType(id);
					t.setTypedef(true);
					t.setSize(baseType.getSize());
					FuncPtrType tmp = (FuncPtrType) baseType;
					((FuncPtrType) t).setParamList(tmp.getParamList());
					((FuncPtrType) t).setHasReturnStmt(tmp.hasReturnStmt());
					((FuncPtrType) t).setReturnByRef(tmp.isReturnByRef());
					((FuncPtrType) t).setReturnType(tmp.getReturnType());
				} 
				else if (baseType.isPointerType()) {
					t = new PointerType(id);
					t.setTypedef(true);
					t.setSize(baseType.getSize());
					PointerType tmp = (PointerType) baseType;
					((PointerType) t).setBaseType(tmp.getBaseType());
					((PointerType) t).setSize(tmp.getSize());
				} 
				else {
					t = new StructType(id);
					t.setSize(baseType.getSize());
					StructType tmp = (StructType) baseType;
					((StructType) t).setBaseName(tmp.getBaseName());
					((StructType) t).setSize(tmp.getSize());
					((StructType) t).setScope(tmp.getScope());
				}
				TypedefSTO sto = new TypedefSTO(id, t);
				m_symtab.insert(sto);
			}
		}
	}

	Type GetTypedefBaseType(STO sto) {
		if (sto.isError()) {
			return null;
		} else {
			return sto.getType();
		}
	}

	// ----------------------------------------------------------------
	//
	// ----------------------------------------------------------------
	void DoStructdefDecl(String id) {
		STO sto = m_symtab.accessLocal(id);
		
		int structSize = 0;
		Scope s = m_symtab.getStructScope();
		Vector<STO> v = s.getList();
		for (int i = 0; i < v.size(); i++) {
			if(!v.get(i).getType().isFuncPtrType()) {
				v.get(i).setOffset(String.valueOf(structSize));
				structSize += v.get(i).getType().getSize();
			}
		}
		((StructType)sto.getType()).setScope(s);
		((StructType)sto.getType()).setSize(structSize);
		setStructName("") ; // when we finish struct declaration, set StructName to empty string
		m_symtab.setStructScope(null);
	}

	void SetStructScope() {
		m_symtab.setStructScope(m_symtab.getCurrentScope());
	}
	
	void DoStructFieldSize() {
		Scope s = m_symtab.getStructScope();
		Vector<STO> v = s.getList();
		int structSize = 0;
		for (int i = 0; i < v.size(); i++) {
			if(!v.get(i).getType().isFuncPtrType()) {
				v.get(i).setOffset(String.valueOf(structSize));
				structSize += v.get(i).getType().getSize();
			}
		}
	}
	
	
	void DoInsertStruct(String id) {
		if (m_symtab.accessLocal(id) != null) {
			m_nNumErrors++;
			m_errors.print(Formatter.toString(ErrorMsg.redeclared_id, id));
		}
		StructType st = new StructType(id);
		st.setBaseName(id);
		TypedefSTO sto = new TypedefSTO(id, st);
		m_symtab.insert(sto);
		setStructName(id);
	}

	// ----------------------------------------------------------------
	//
	// ----------------------------------------------------------------
	void DoFuncDecl_1(String id, Type returnType, String isRef) {
		if (m_symtab.accessLocal(id) != null) {
			m_nNumErrors++;
			if (!isStruct()) {
				m_errors.print(Formatter.toString(ErrorMsg.redeclared_id, id));
			} else {
				m_errors.print(Formatter.toString(ErrorMsg.error13a_Struct, id));
			}
		}
		else {
			FuncSTO sto;
			if (isRef != null) {
				sto = new FuncSTO(id, returnType, true);
			} else {
				sto = new FuncSTO(id, returnType, false);
			}
			m_symtab.insert(sto);
			m_symtab.openScope();
			m_symtab.setFunc(sto);
		}
	}

	// ----------------------------------------------------------------
	//
	// ----------------------------------------------------------------
	void DoFuncDecl_2() {
		// check if the func have return statement
		// add new attribute to scope.java.
		// symtab.getLevel() it must be 1

		// Level 2 is func scope
		if (m_symtab.getLevel() == 2 && !m_symtab.getFunc().getReturnType().isVoid()) {
			if (!m_symtab.getCurrentScopeReturn()) {
				m_nNumErrors++;
				m_errors.print(ErrorMsg.error6c_Return_missing);
			}
		}
		m_symtab.closeScope();
		cg.printFuncStart(m_symtab.getFunc().getName(), isStruct(), getStructName());
		cg.printFuncEnd(m_symtab.getFunc(), getLocalVarSize(), isStruct(), getStructName());
		setLocalVarSize(0);
		m_symtab.setFunc(null);
	}

	// ----------------------------------------------------------------
	//
	// ----------------------------------------------------------------
	void DoFormalParams(Vector<STO> params) {
		int paramSize = 0;
		if (m_symtab.getFunc() == null) {
			m_nNumErrors++;
			m_errors.print("internal: DoFormalParams says no proc!");
		}
		if(params != null) {
			for(int i = 0; i < params.size(); i++) {
				String offset = String.valueOf(72 + paramSize);
				params.get(i).setBase(LOCAL);
				params.get(i).setOffset(offset);
				paramSize += params.get(i).getType().getSize();
			}
		}
		m_symtab.getFunc().setParamList(params);
		if (params != null) {
			for (int i = 0; i < params.size(); i++) {
				if (params.get(i).isError()) {
					continue;
				}
				m_symtab.insert(params.get(i));
			}
		}
	}

	// ----------------------------------------------------------------
	//
	// ----------------------------------------------------------------
	void DoBlockOpen() {
		// Open a scope.
		m_symtab.openScope();
	}

	// ----------------------------------------------------------------
	//
	// ----------------------------------------------------------------
	void DoBlockClose() {
		m_symtab.closeScope();
	}

	// ----------------------------------------------------------------
	// CHECK 3: ASSIGNMENT OPERATOR
	// CHECK 3A: ASSIGNABILITY
	// CHECK 3B: TYPE CONFLICT
	// ----------------------------------------------------------------
	// DoDeclType checking
	STO DoAssignExpr(STO left, STO right) // left = stoDes
	{
		STO sto = null;
		if (left.isError()) {
			return left;
		}
		if (right.isError()) {
			return right;
		}
		// a value is not assignable when it is not a modifiable L-value
		if (!left.isModLValue()) {
			m_nNumErrors++;
			m_errors.print(ErrorMsg.error3a_Assign);
			return new ErrorSTO("error3a_Assign");
		}

		// Check 3b: Type conflict
		// if they are not the same type, check if they are numeric
		if (!(left.getType().isBasic() && right.getType().isBasic())) { // PointerType,
																		// ArrayType,
																		// or
																		// StructType
			if (!right.getType().isAssignableTo(left.getType())) {
				m_nNumErrors++;
				m_errors.print(Formatter.toString(ErrorMsg.error3b_Assign,
						right.getType().getName(), left.getType().getName()));
				sto = new ErrorSTO("error3b_Assign");
			} else {
				sto = new ExprSTO(left.getName() + "=" + right.getName(), left.getType());
				sto.setIsRValue();
			}
		} 
		else {
			if (left.getType().isEquivalentTo(right.getType())) {
				if (left.getType().isBool()) {
					sto = new ExprSTO(left.getName() + "=" + right.getName(), new BoolType());
					sto.setIsRValue();
				} 
				else if (left.getType().isInt()) {
					sto = new ExprSTO(left.getName() + "=" + right.getName(), new IntType());
					sto.setIsRValue();
				} 
				else { // float
					sto = new ExprSTO(left.getName() + "=" + right.getName(), new FloatType());
					sto.setIsRValue();
				}
			}
			// check if they are both numeric
			else if (left.getType().isNumeric() && right.getType().isNumeric()) {
				if (left.getType().isFloat()) {
					sto = new ExprSTO(left.getName() + "=" + right.getName(), new FloatType());
					sto.setIsRValue();
				} 
				else {
					m_nNumErrors++;
					m_errors.print(Formatter
							.toString(ErrorMsg.error3b_Assign, right.getType()
									.getName(), left.getType().getName()));
					sto = new ErrorSTO("error3b_Assign");
				}
			} 
			else { // error
				m_nNumErrors++;
				m_errors.print(Formatter.toString(ErrorMsg.error3b_Assign,
						right.getType().getName(), left.getType().getName()));
				sto = new ErrorSTO("error3b_Assign");
			}
		}
		setLocalVarSize(getLocalVarSize() + right.getType().getSize());
		sto.setBase(LOCAL);
		sto.setOffset("-" + String.valueOf(getLocalVarSize()));
		if (m_symtab.getLevel() == 1 ) {
			cg.printAssign(left, right, sto, true); // in global
		} 
		else {
			cg.printAssign(left, right, sto, false); // in local 
		}
		return sto;
	}

	// ----------------------------------------------------------------
	//
	// ----------------------------------------------------------------
	// check Decltype
	STO DoFuncCall(STO sto, Vector<STO> argList) {
		Vector<STO> paramList = null;
		if (sto.isError()) {
			return sto;
		}
		if (!sto.isFunc() && !sto.getType().isFuncPtrType()) {
			m_nNumErrors++;
			m_errors.print(Formatter.toString(ErrorMsg.not_function, sto.getName()));
			return new ErrorSTO(sto.getName());
		} 
		else {
			paramList = ((FuncPtrType) sto.getType()).getParamList();
			// check if argList is null or not before check the size
			if (argList == null && paramList != null) {
				m_nNumErrors++;
				m_errors.print(Formatter.toString(ErrorMsg.error5n_Call, 0, paramList.size()));
				return new ErrorSTO("error5n_Call");
			}
			if (argList != null && paramList == null) {
				m_nNumErrors++;
				m_errors.print(Formatter.toString(ErrorMsg.error5n_Call, argList.size(), 0));
				return new ErrorSTO("error5n_Call");
			}
			if (argList != null && paramList != null) {
				// if the number of param and arg differs
				if (paramList.size() != argList.size()) {
					m_nNumErrors++;
					m_errors.print(Formatter.toString(ErrorMsg.error5n_Call, argList.size(), paramList.size()));
					return new ErrorSTO("error5n_Call");
				}

				// same number of arguments as param passed in, check if any
				// error in argList
				for (int i = 0; i < argList.size(); i++) {
					if (argList.get(i).isError()) {
						return argList.get(i);
					}
				}
				for (int i = 0; i < paramList.size(); i++) {
					Type argListType = argList.get(i).getType();
					Type paramListType = paramList.get(i).getType();

					// Do special checking for ArrayType params
					if (argListType.isArrayType() && paramListType.isArrayType()) {
						boolean isRef = ((VarSTO) paramList.get(i)).isRef();
						boolean success = DoArrayFuncCall((ArrayType) argListType,
								(ArrayType) paramListType, isRef, paramList.get(i).getName());
						if (!success) {
							return new ErrorSTO("error5c_Call");
						}
						continue;
					}

					/* type check the arguments passed in to the function one by
					 * one against the paramList stored in FuncSTO, if they are
					 * different, throw error message right away and stop
					 * checking
					 */
					if (((VarSTO) paramList.get(i)).isRef()) {
						// check equivalent for param
						if (!argList.get(i).getType().isEquivalentTo(paramList.get(i).getType())) {
							m_nNumErrors++;
							m_errors.print(Formatter.toString(
									ErrorMsg.error5r_Call, argListType.getName(), paramList.get(i)
											.getName(), paramListType.getName()));
							return new ErrorSTO("error5r_Call");
						}
						if (!argList.get(i).isModLValue()) {
							m_nNumErrors++;
							m_errors.print(Formatter.toString(
									ErrorMsg.error5c_Call, paramList.get(i).getName(), paramListType.getName()));
							return new ErrorSTO("error5c_Call");
						}
					} 
					else {
						if (!(argListType.isAssignableTo(paramListType))) {
							m_nNumErrors++;
							m_errors.print(Formatter.toString(
									ErrorMsg.error5a_Call, argList.get(i).getType().getName(), paramList
											.get(i).getName(), paramList.get(i).getType().getName()));
							return new ErrorSTO("error5a_Call");
						}
					}
				}
			}
		}
		// at this point, everything is fine(and we are sure that sto is a
		// FuncSTO/FuncPtrType,
		// create an ExprSTO and return it up as the result of our function call
		ExprSTO exprSTO = new ExprSTO("FuncCall", ((FuncPtrType) sto.getType()).getReturnType());
		if(exprSTO.getType().isPointerType()) {
			exprSTO.setAddressOfTypeCast(true);
			exprSTO.setRef(true);
		}
		if (((FuncPtrType) sto.getType()).isReturnByRef()) {
			exprSTO.setIsModLValue();
			exprSTO.setRef(true);
		} 
		else {
			exprSTO.setIsRValue();
		}
		if (!isDecltypeAppear()) { // check if it is in Decltype 
			if (argList == null) { 			// no param for function
				setLocalVarSize(getLocalVarSize() + exprSTO.getType().getSize());
				exprSTO.setBase(LOCAL);
				exprSTO.setOffset("-" + String.valueOf(getLocalVarSize()));
				cg.printDoCallNoParam(exprSTO, sto, getLocalVarSize());
			} 
			else {	// params in function
				setLocalVarSize(getLocalVarSize() + exprSTO.getType().getSize());
				exprSTO.setBase(LOCAL);
				exprSTO.setOffset("-" + String.valueOf(getLocalVarSize()));
				cg.printDoCallWithParam(exprSTO, sto, getLocalVarSize(), argList, paramList);
			}
		}
		return exprSTO;
	}

	boolean DoArrayFuncCall(ArrayType argListType, ArrayType paramListType, boolean isRef, String paramName) {
		if (isRef) {
			if (!argListType.isEquivalentTo(paramListType)) {
				m_nNumErrors++;
				m_errors.print(Formatter.toString(ErrorMsg.error5r_Call, argListType.getName(), paramName, 
						       paramListType.getName()));
				return false;
			}
		} 
		else { // pass by value
			if (!argListType.isAssignableTo(paramListType)) {
				m_nNumErrors++;
				m_errors.print(Formatter.toString(ErrorMsg.error5a_Call, argListType.getName(), paramName,
						       paramListType.getName()));
				return false;
			}
		}
		return true;
	}

	// ----------------------------------------------------------------
	//
	// ----------------------------------------------------------------
	STO DoDesignator2_Dot(STO sto, String strID) {
		// Good place to do the struct checks
		if (sto.isError()) {
			return sto;
		}
		STO structField;
		/************************** CHECK 14: STRUCT/THIS ***************************/
		if (sto.isConst() && sto.getName().equals("this")) {
			Scope scope = m_symtab.getStructScope();
			if ((structField = scope.access(strID)) == null) {
				// throw error
				m_nNumErrors++;
				m_errors.print(Formatter.toString(ErrorMsg.error14b_StructExpThis, strID));
				return new ErrorSTO("error14b_StructExpThis");
			}
		} 
		else {
			if (!sto.getType().isStructType()) {
				// throw error
				m_nNumErrors++;
				m_errors.print(Formatter.toString(ErrorMsg.error14t_StructExp, sto.getType().getName()));
				return new ErrorSTO("error14t_StructExp");
			}
			Scope s = ((StructType) sto.getType()).getScope();
			if ((structField = s.access(strID)) == null) {
				m_nNumErrors++;
				m_errors.print(Formatter.toString(ErrorMsg.error14f_StructExp, strID, sto.getType().getName()));
				return new ErrorSTO("error14f_StructExp");
			}
		}
		// structField stores the offset of the field
		if(structField.getType().isArrayType()) {
			setLocalVarSize(getLocalVarSize() + ((ArrayType)structField.getType()).getBaseType().getSize());
		}
		else {
			setLocalVarSize(getLocalVarSize() + structField.getType().getSize());
		}
		ExprSTO result;
		if(!(sto.getName().equals("this") && sto.isConst())) {
			result = new ExprSTO("." + sto.getType().getName() + "." + strID, structField.getType());
		}
		else {
			if(structField.getType().isFuncPtrType()) {
				result = new ExprSTO("." + getStructName() + "." + strID, structField.getType());
			}
			else {
				result = new ExprSTO(sto.getName() + "." + strID, structField.getType());
			}
		}
		result.setRef(true);
		result.setBase(LOCAL);
		result.setOffset("-" + getLocalVarSize());
		result.setIsModLValue();
		cg.structAccess(sto, structField, result);
		return result;
	}

	// ----------------------------------------------------------------
	//
	// ----------------------------------------------------------------
	STO DoDesignator2_Array(STO des, STO expr) {
		// Good place to do the array checks
		if (des.isError()) {
			return des;
		}
		if (expr.isError()) {
			return expr;
		}
		Type correctType = new IntType();
		if (!des.getType().isArrayType() && !des.getType().isPointerType()) {
			m_nNumErrors++;
			m_errors.print(Formatter.toString(ErrorMsg.error11t_ArrExp, des.getType().getName()));
			return new ErrorSTO("error11t_ArrExp");
		}
		if (!(expr.getType().isEquivalentTo(correctType))) {
			m_nNumErrors++;
			m_errors.print(Formatter.toString(ErrorMsg.error11i_ArrExp, expr
					.getType().getName()));
			return new ErrorSTO("error11i_ArrExp");
		}

		// if the index expr is constant, check the bounds of the array
		if (expr.isConst()) {
			ConstSTO cExpr = (ConstSTO) expr;
			// only check bounds when designator is ArrayType(not Ptrtype)
			if (des.getType().isArrayType()) {
				ArrayType desType = (ArrayType) des.getType();
				if (cExpr.getIntValue() < 0 || cExpr.getIntValue() >= desType.getNumOfElements()) {
					m_nNumErrors++;
					m_errors.print(Formatter.toString(ErrorMsg.error11b_ArrExp, cExpr.getIntValue(), desType.getNumOfElements()));
					return new ErrorSTO("error11b_ArrExp");
				}
			}
		}
		ExprSTO result;
		if(des.getType().isArrayType()) {
			result = new ExprSTO(des.getName() + "[" + expr.getName() + "]", ((ArrayType) des.getType()).getBaseType());
			setLocalVarSize(getLocalVarSize() + ((ArrayType)des.getType()).getBaseType().getSize());
		} 
		else {
			result = new ExprSTO(des.getName() + "[" + expr.getName() + "]", ((PointerType) des.getType()).getBaseType());
			setLocalVarSize(getLocalVarSize() + 4);
		}
		result.setRef(true);
		result.setIsModLValue();
		result.setBase(LOCAL);
		result.setOffset("-" + getLocalVarSize());
		if(m_symtab.getLevel() == 1) {
			cg.printArrayIndexing(des, expr, result, true);
		}
		else {
			cg.printArrayIndexing(des, expr, result, false);
		}
		return result;
	}

	// ----------------------------------------------------------------
	//
	// ----------------------------------------------------------------
	STO DoDesignator3_ID(String strID) {
		STO sto;
		if(isStruct()) {
			if (m_symtab.getFunc()!= null) { // if it is a func in struct, check func Scope and global
				if  ( ((sto = m_symtab.accessStructScope(strID)) != null) ) {
					return sto ;
				} else if (((sto = m_symtab.accessGlobal(strID)) != null)) { 
					return sto ;
				} else { // cant find variable in both func scope and global 
					m_nNumErrors++;
					m_errors.print(Formatter.toString(ErrorMsg.undeclared_id, strID));
					sto = new ErrorSTO(strID);
				}
				
			}  else {
				if ((sto = m_symtab.accessGlobal(strID)) == null) {
					m_nNumErrors++;
					m_errors.print(Formatter.toString(ErrorMsg.undeclared_id, strID));
					sto = new ErrorSTO(strID);
				}
			}
		}
		else {
			if ((sto = m_symtab.access(strID)) == null) {
				m_nNumErrors++;
				System.out.println("strID " + strID) ;
				m_errors.print(Formatter.toString(ErrorMsg.undeclared_id, strID));
				sto = new ErrorSTO(strID);
			}
		}
		return sto;
	}

	// ----------------------------------------------------------------
	//
	// ----------------------------------------------------------------
	STO DoQualIdent(String strID) {
		STO sto;
		if ((sto = m_symtab.access(strID)) == null) {
			m_nNumErrors++;
			m_errors.print(Formatter.toString(ErrorMsg.undeclared_id, strID));			
			return new ErrorSTO(strID);
		}
		if (!sto.isTypedef()) {
			m_nNumErrors++;
			m_errors.print(Formatter.toString(ErrorMsg.not_type, sto.getName()));
			return new ErrorSTO(sto.getName());
		}
		return sto;
	}

	/****************************** CHECK 0: GLOBAL IDENTIFIER ::x **************************/
	// ----------------------------------------------------------------
	// Check if the variable exists in Global scope or not when
	// accessed with ::
	// ----------------------------------------------------------------

	STO DoGlobalVarAccess(String strID) {
		STO sto;
		if ((sto = m_symtab.accessGlobal(strID)) == null) {
			m_nNumErrors++;
			m_errors.print(Formatter.toString(ErrorMsg.error0g_Scope, strID));
			sto = new ErrorSTO(strID);
		}

		return sto;
	}

	/********************************** CHECK 1: BINARY OPERATORS *****************************/
	// ----------------------------------------------------------------
	// Check if arithmetic operation exists a semantic error
	// ----------------------------------------------------------------
	STO DoBinaryExpr(STO left, Operator o, STO right) {
		if (left.isError())
			return left;
		if (right.isError())
			return right;

		STO sto = o.checkOperands(left, right);
		Type l_type = left.getType();
		Type r_type = right.getType();

		if (sto.isError()) {
			m_nNumErrors++;
			if (o.isModOp()) {
				Type correctType = new IntType();
				// if left operand's type is not int, print error message with left's type;
				// print right operand's type otherwise.
				if (!left.getType().isInt()) {
					m_errors.print(Formatter.toString(ErrorMsg.error1w_Expr,
							l_type.getName(), o.getOpName(), correctType.getName()));
				} 
				else {
					m_errors.print(Formatter.toString(ErrorMsg.error1w_Expr,
							r_type.getName(), o.getOpName(), correctType.getName()));
				}
			} 
			else if (o.isEqualToOp() || o.isNotEqualToOp()) {
				if (left.getType().isPointerType() || right.getType().isPointerType()) {
					m_errors.print(Formatter.toString(ErrorMsg.error17_Expr, o.getOpName(), l_type.getName(), r_type.getName()));
				} 
				else {
					m_errors.print(Formatter.toString(ErrorMsg.error1b_Expr, l_type.getName(), o.getOpName(), r_type.getName()));
				}
			} 
			else if (o.isBooleanOp()) {
				Type correctType = new BoolType();
				m_errors.print(Formatter.toString(ErrorMsg.error1w_Expr,
						l_type.getName(), o.getOpName(), correctType.getName()));
			} 
			else if (o.isBitwiseOp()) {
				Type correctType = new IntType();
				if (!l_type.isInt()) {
					m_errors.print(Formatter.toString(ErrorMsg.error1w_Expr,
							l_type.getName(), o.getOpName(), correctType.getName()));
				} 
				else {
					m_errors.print(Formatter.toString(ErrorMsg.error1w_Expr, r_type.getName(), o.getOpName(), correctType.getName()));
				}
			} 
			else { // ArithmeticOp or < or > or <= or >=
				// if left operand's type is not int/float, print error message with left's type;
				// print right operand's type otherwise.
				if (!left.getType().isNumeric()) {
					m_errors.print(Formatter.toString(ErrorMsg.error1n_Expr, l_type.getName(), o.getOpName()));
				} 
				else {
					m_errors.print(Formatter.toString(ErrorMsg.error1n_Expr, r_type.getName(), o.getOpName()));
				}
			}
			return sto;
		}

		// No error, proceed to see if we need to perform constant folding
		/************************* CHECK 8: CONSTANT FOLDING ************************************/
		if ((left.isConst() && right.isConst()) && !right.getType().isNullPtrType() && !o.isBooleanOp()) {
			sto = o.calculate((ConstSTO) left, (ConstSTO) right);
			if (sto.isError()) { // Catch Arithmetic exceptions
				m_nNumErrors++;
				m_errors.print(ErrorMsg.error8_Arithmetic);
			}
		}
		else {
			if (!isDecltypeAppear()) {
				setLocalVarSize(getLocalVarSize() + sto.getType().getSize());
				// store the result of any arithmetic operation on the runtime stack
				sto.setName(left.getName() + o.getOpName() + right.getName());
				sto.setBase(LOCAL);
				sto.setOffset("-" + String.valueOf(getLocalVarSize()));
				if (m_symtab.getFunc() != null) {
					cg.printBinaryExpr(left, o, right, sto, false);
				}
				else {
					cg.printBinaryExpr(left, o, right, sto, true);
				}
			}
		}
		return sto;
	}

	STO DoUnaryExpr(Operator o, STO op, boolean prefix) {
		STO sto;
		Type op_type = op.getType();
		if (op.isError()) {
			return op;
		}
		sto = o.unaryOperand(op);
		if (sto.isError()) {
			m_nNumErrors++;
			if (o.isIncOp() || o.isDecOp()) {
				if (!op_type.isNumeric() && !op_type.isPointerType()) {
					m_errors.print(Formatter.toString(ErrorMsg.error2_Type, op_type.getName(), o.getOpName()));
					return sto;
				}
				if (!op.isModLValue()) {
					m_errors.print(Formatter.toString(ErrorMsg.error2_Lval, o.getOpName()));
					return sto;
				}
			} 
			else { // it must be a NotOp
				Type correctType = new BoolType();
				m_errors.print(Formatter.toString(ErrorMsg.error1u_Expr, op_type.getName(), o.getOpName(), correctType.getName()));
				return sto;
			}
		}
		/**************************** CHECK 8: CONSTANT FOLDING ************************************/
		if (!isDecltypeAppear()) {
			setLocalVarSize(getLocalVarSize() + sto.getType().getSize());
			// store the result of any arithmetic operation on the runtime stack
			sto.setName(op.getName() + o.getOpName());
			sto.setBase(LOCAL);
			String offset = "-" + String.valueOf(getLocalVarSize());
			sto.setOffset(offset);
			if(o.isIncOp() || o.isDecOp()) {
				if(m_symtab.getFunc() == null) {
					cg.printIncDec(o, op, LOCAL, offset, prefix, true);
				}
				else {
					cg.printIncDec(o, op, LOCAL, offset, prefix, false);
				}
			}
			else {
				if(m_symtab.getFunc() == null) {
					cg.printUnaryExpr(op, o, LOCAL, offset, true);
				}
				else {
					cg.printUnaryExpr(op, o, LOCAL, offset, false);
				}
			}
		}
		return sto;
	}

	STO DoUnarySign(STO op, String sign) {
		STO sto;
		if (op.isError()) {
			return op;
		}
		if (sign.equals("+")) {
			sto = DoBinaryExpr(op, new MulOp(), new ConstSTO("1", new IntType(), "1"));
		} 
		else {
			sto = DoBinaryExpr(op, new MulOp(), new ConstSTO("-1", new IntType(), "-1"));
		}
		return sto;
	}

	/************************ CHECK 3C: DECLTYPE *************************************************/
	Type DoDecltype(STO expr) {
		return expr.getType();
	}

	/***************************** CHECK 4: IF/WHILE TYPE CONFLICT ******************************/
	void DoIfWhileCheck(STO expr) {
		if (expr.isError()) {
			return;
		}
		if (!expr.getType().isBool() && !expr.getType().isInt()) {
			m_nNumErrors++;
			m_errors.print(Formatter.toString(ErrorMsg.error4_Test, expr.getType().getName()));
		}
	}
	
	void DoIfStatement(STO expr) {
		cg.printIfStatement(expr, m_symtab.getFunc().getName());
	}
	
	void DoPrintIfStmtLabel() {
		cg.popIfStmtLabel();
	}
	
	void DoElse() {
		cg.printElse();
	}

	/*************************** CHECK 6a/b: DETECT ILLEGAL RETURN STATEMENT *************************/
	void DoVoidReturnCheck() {
		DoMarkReturnStmt();
		if (!(m_symtab.getFunc().getReturnType().getName().equals("void"))) {
			m_nNumErrors++;
			m_errors.print(ErrorMsg.error6a_Return_expr);
		}
	}

	void DoReturnCheck(STO sto) {
		if (sto.isError()) {
			return;
		}
		DoMarkReturnStmt();
		FuncSTO func = m_symtab.getFunc();
		if (func.isReturnByRef()) {
			if (!(sto.getType().isEquivalentTo(func.getReturnType()))) {
				m_nNumErrors++;
				m_errors.print(Formatter.toString(ErrorMsg.error6b_Return_equiv, sto.getType().getName(),
						func.getReturnType().getName()));
				return;
			}
			if (!sto.isModLValue()) {
				m_nNumErrors++;
				m_errors.print(ErrorMsg.error6b_Return_modlval);
				return;
			}
		} 
		else { // return by value
			if (!(sto.getType().isAssignableTo(func.getReturnType()))) {
				m_nNumErrors++;
				m_errors.print(Formatter.toString(ErrorMsg.error6a_Return_type, sto.getType().getName(), func.getReturnType().getName()));
			}
		}
	}

	/****************************** CHECK 6c: DETECT MISSING RETURN STATEMENT ***********************/
	void DoMarkReturnStmt() {
		m_symtab.setScopeReturn();
	}
	
	void DoPrintReturnStmt(STO expr) {
		FuncSTO funcSTO = m_symtab.getFunc();
		cg.printReturnStmt(funcSTO, expr);
	}

	/****************************** CHECK 7: DETECT ILLEGAL EXIT STATEMENT **************************/
	void DoExitCheck(STO sto) {
		if (sto.isError()) {
			return;
		}
		Type intType = new IntType();
		if (!(sto.getType().isAssignableTo(intType))) {
			m_nNumErrors++;
			m_errors.print(Formatter.toString(ErrorMsg.error7_Exit, sto
					.getType().getName()));
		}
	}
	
	void DoExitStmt(STO sto) {
		cg.printExitStmt(sto);
	}

	/******************************** CHECK 10 Check Array Declaration *****************************/
	Type DoArrayDeclCheck(STO sto) {
		if (sto.isError()) {
			return null;
		}
		Type correctType = new IntType();
		if (!sto.getType().isEquivalentTo(correctType)) {
			// throw error
			m_nNumErrors++;
			m_errors.print(Formatter.toString(ErrorMsg.error10i_Array, sto
					.getType().getName()));
			return null;
		}
		if (!sto.isConst()) {
			m_nNumErrors++;
			m_errors.print(ErrorMsg.error10c_Array);
			return null;
		}
		int index = ((ConstSTO) sto).getIntValue();
		if (index <= 0) {
			m_nNumErrors++;
			m_errors.print(Formatter.toString(ErrorMsg.error10z_Array, index));
			return null;
		}
		ArrayType t = new ArrayType();
		t.setNumOfElements(((ConstSTO) sto).getIntValue());
		return t;
	}

	Type DoTypedefArray(Type subType, Type arrayType, Type ptrType) {
		Type newType = subType;
		if (subType == null) { // Typedef error
			return null;
		}
		if (ptrType != null) {
			DoSetPointerBaseType((PointerType) ptrType, subType,0);
			newType = ptrType;
		}
		if (arrayType == null) {
			return newType;
		}
		ArrayType result = new ArrayType();
		result.setBaseType(newType);
		ArrayType tmp = (ArrayType) arrayType;
		result.setNumOfElements(tmp.getNumOfElements());
		result.setName(newType.getName() + "[" + tmp.getNumOfElements() + "]");
		return result;

	}
	
	/*************************** CHECK 12B: Break/Continue Check *********************************/
	void DoWhileForEachCheck_1() {
		if (!getWhileForEachOn()) {
			setWhileForEachOn(true);
			setWhileForEachLevel(m_symtab.getLevel() + 1);
		}
	}

	void DoWhileForEachCheck_2() {
		if (m_symtab.getLevel() == getWhileForEachLevel() - 1) {
			setWhileForEachOn(false);
			setWhileForEachLevel(-1);
		}
	}
	
	void DoWhile1() {
		cg.printWhile1();
	}
	
	void DoWhile2(STO expr) {
		cg.printWhile2(expr);
	}
	
	void DoWhile3() {
		cg.printWhile3();
	}

	void DoBreakContinueCheck(boolean isBreak) {
		if (!getWhileForEachOn()) { // no while/for each but has break/continue stmt
			m_nNumErrors++;
			if (isBreak) {
				m_errors.print(ErrorMsg.error12_Break);
			} else {
				m_errors.print(ErrorMsg.error12_Continue);
			}
		} 
		else {
			if (m_symtab.getLevel() < getWhileForEachLevel()) {
				m_nNumErrors++;
				if (isBreak) {
					m_errors.print(ErrorMsg.error12_Break);
				} else {
					m_errors.print(ErrorMsg.error12_Continue);
				}
			}
		}
	}
	
	void DoBreak() {
		cg.printBreak();
	}
	
	void DoContinue() {
		cg.printContinue();
	}

	void DoAuto(String id, STO expr, boolean isConst, boolean isStatic) {
		if (expr.isError())
			return;
		IDInfo i;
		Type isArray = null;
		Type isPtr = null;
		Type exprType = expr.getType();
		if (expr.getType().isArrayType()) {
			isArray = expr.getType();
		}
		if (expr.getType().isPointerType()) {
			isPtr = expr.getType();
		}
		if (isArray != null) {
			exprType = ((ArrayType) expr.getType()).getBaseType();
		}
		i = new IDInfo(isArray, id, expr, isPtr);
		Vector<IDInfo> v = new Vector<IDInfo>();
		v.add(i);

		if (isConst) {
			DoConstDecl(exprType, v, isStatic);
		} 
		else {
			DoVarDecl(v, exprType, isStatic);
		}
	}

	/*************************** CHECK 15: POINTERS(DEREFERENCE/DOT/ARROW OPERATIONS) *********************************/
	STO DoDereference(STO des) {
		ExprSTO result;
		boolean isGlobal;
		if (des.isError()) {
			return des;
		}
		if (!des.getType().isPointerType()) {
			m_nNumErrors++;
			m_errors.print(Formatter.toString(ErrorMsg.error15_Receiver, des.getType().getName()));
			return new ErrorSTO("error15_Receiver");
		} 
		else {
			setLocalVarSize(getLocalVarSize() + ((PointerType) des.getType()).getBaseType().getSize());
			result = new ExprSTO("*" + des.getName(), ((PointerType) des.getType()).getBaseType());
			result.setOffset("-" + getLocalVarSize());
			if(m_symtab.getFunc() == null) {
				result.setBase(GLOBAL);
				isGlobal = true;
			}
			else {
				result.setBase(LOCAL);
				isGlobal = false;
			}
			result.setRef(true);
			result.setIsModLValue();
		}
		cg.printDereference(des, result, isGlobal);
		return result;
	}

	STO DoArrow(STO sto, String id) {
		if (sto.isError()) {
			return sto;
		}
		if (!sto.getType().isPointerType()) {
			m_nNumErrors++;
			m_errors.print(Formatter.toString(ErrorMsg.error15_Receiver, sto.getType().getName()));
			return new ErrorSTO("error15_ReceiverArrow");

		} 
		else {
			PointerType ptr = (PointerType) sto.getType();
			if (!ptr.getBaseType().isStructType()) {
				m_nNumErrors++;
				m_errors.print(Formatter.toString(ErrorMsg.error15_ReceiverArrow, sto.getType().getName()));
				return new ErrorSTO("error15_ReceiverArrow");
			}
		}
		// arrow is a dereference and a dot, so need to get result from
		// dereferencing then pass the result to the dot method
		STO starResult = DoDereference(sto);
		if (starResult.isError()) {
			return starResult;
		}
		STO dotResult = DoDesignator2_Dot(starResult, id);
		return dotResult;
	}

	/*************************** CHECK 16: NEW/DELETE CHECK *********************************/
	void DoNewDelete(STO des, boolean isNew) {
		if (!des.isModLValue()) {
			m_nNumErrors++;
			if (isNew) {
				m_errors.print(ErrorMsg.error16_New_var);
			} 
			else {
				m_errors.print(ErrorMsg.error16_Delete_var);
			}
		}
		if (!des.getType().isPointerType()) {
			m_nNumErrors++;
			if (isNew) {
				m_errors.print(Formatter.toString(ErrorMsg.error16_New, des.getType().getName()));
			}
			else {
				m_errors.print(Formatter.toString(ErrorMsg.error16_Delete, des.getType().getName()));
			}
		}
		
		if (isNew){ // new
			cg.printNew(des);
		}
		else { // free 
			cg.printDelete(des);
		}
	}
	
	/*********************************CHECK 19: SIZEOF **************************************/
	public STO DoSizeOf(STO des) {
		if(des.isError()) {	
			return des;
		} 
		if(!des.isAddressable()) {
			m_nNumErrors++;
			m_errors.print(ErrorMsg.error19_Sizeof);
			return new ErrorSTO("error19_Sizeof");
		}
		int size = des.getType().getSize();
		ConstSTO res = new ConstSTO("sizeof(" + des.getName() +")", new IntType(), size);
		return res;
	} 
	
	public STO DoSizeOf(Type type) {
		int size = type.getSize();
		ConstSTO res = new ConstSTO("sizeof(" + type.getName() +")", new IntType(), size);
		return res;
	} 
	
	/*********************************CHECK 20: TypeCast*************************************/
	public STO DoTypeCast(Type type, STO des) {
		if (des.isError()) {
			return des;
		}
		
		STO result = null;
		Type desType = des.getType() ;
		if (desType.isArrayType() || desType.isStructType()|| desType.isFuncPtrType()|| type.isFuncPtrType()){
			m_nNumErrors++;
			m_errors.print(Formatter.toString(ErrorMsg.error20_Cast,des
					.getType().getName(), type.getName()));
			return new ErrorSTO("error20_Cast");
		}
		if (des.isConst()) {
			Double r_value = 0.0;
			ConstSTO cDes = (ConstSTO) des;
			if(cDes.getType().isBool() && ( type.isInt()|| type.isFloat() || type.isPointerType()) ) {
				// case for casting bool to int/ float/ ptr 
				if (cDes.getBoolValue()){
					r_value = 1.0;
				} 
				else {
					r_value = 0.0;
				}
				result = (ConstSTO) new ConstSTO (cDes.getName(), type, Double.toString(r_value));
			} 
			if (( cDes.getType().isInt()|| cDes.getType().isFloat() || cDes.getType().isPointerType()) && type.isBool()){
				// case for casting int float ptr to bool
				if (cDes.getValue() != 0 ) {
					r_value = 1.0;
				} 
				else {
					r_value = 0.0;
				} 
				if (r_value == 0) { 
					result = (ConstSTO) new ConstSTO (cDes.getName(), type, Double.toString(r_value));
				} 
				else {
					result = (ConstSTO) new ConstSTO (cDes.getName(), type, Double.toString(r_value));
				}
				
			}
			if (cDes.getType().isFloat() && (type.isInt() || type.isPointerType())) {
				// case for casting float to int and ptr
				r_value = (double)cDes.getIntValue();
				result = (ConstSTO) new ConstSTO (cDes.getName(), type, Double.toString(r_value));	
			}
			if ((cDes.getType().isInt() && type.isPointerType()) || ( type.isInt() && cDes.getType().isPointerType())) {
				// case for int <--> ptr
				r_value = (double) cDes.getIntValue();				
				result = (ConstSTO) new ConstSTO (cDes.getName(), type, Double.toString(r_value));
			}
			if (type.isFloat() && (cDes.getType().isInt() || cDes.getType().isPointerType())){
				r_value = (double)cDes.getValue() ;
				result = (ConstSTO) new ConstSTO (cDes.getName(), type, Double.toString(r_value));
			}
			// deal with the case for Constant casting
		} 
		else {
			result = new ExprSTO("(" + type.getName()+")"+ des.getName(), type);
			result.setIsRValue();
		}
		return result;
	}
	
	/**************************************CHECK 21: ADDRESS OF ***********************************/
	STO DoAddressOf(STO des) {
		if (des.isError()){
			return des;
		}
		STO result = null;
		if (!des.isAddressable()) {
			m_nNumErrors++;
			m_errors.print(Formatter.toString(ErrorMsg.error21_AddressOf, des.getType().getName()));
			return new ErrorSTO("error21_AddressOf");
				
		} 
		else {
			PointerType newType = new PointerType();
			newType.setBaseType(des.getType()); // set a basetype to des type
			result = new ExprSTO("&"+des.getName(), newType);
			result.setRef(true);
			result.setAddressOfTypeCast(true);
			result.setIsRValue();
		}
		result.setBase(LOCAL);
		setLocalVarSize(getLocalVarSize() + 4);
		result.setOffset("-"+String.valueOf(getLocalVarSize()));
		if (m_symtab.getLevel()!= 1){
			cg.printAddress(des,result,false);
		}
		else {
			cg.printAddress(des,result,true);
		}
		return result;
	}
	
	void DoCout(STO sto){
		if (!sto.hasAddress()){
			cg.printConstant(sto, m_symtab.getFunc().getName());
		} 
		else {
			cg.printNonConstant(sto);
		}
	}
	
	void DoMarkDeclType (boolean decltype){
		setDecltypeAppear(decltype) ;
	}

	void DoShortCircuit(STO expr, Operator o) {
		if(m_symtab.getLevel() == 1) {
			cg.printShortCircuitLeft(expr, o, true);
		}
		else {
			cg.printShortCircuitLeft(expr, o, false);
		}
	}
	
	
	
	/******************PHASE IIA: CIN *********************/
	void DoCin (STO des) {
		cg.printCin(des);
	}
	
	void DoPopForEachLabel() {
		cg.popForEachLabel();
	}
	
	void DoIncForEach(STO iterVar) {
		cg.incForEach(iterVar);
	}
	void DoForEach(STO iterVar, STO iterable) {
		cg.printForeach(iterVar, iterable);
	}
}
